package model.directory;

import java.lang.reflect.Method;
import java.util.List;

import model.logic.Disjunction;

/**
 * A <tt>ServiceOperation</tt> consists of a Method and 
 * a semantic description of provided post-conditions.
 * @author Marius Tempelmeier
 *
 */
public class ServiceOperation {
	
	private Method method;
	
	private Disjunction annotations;
	
	public ServiceOperation(Method method, Disjunction annotations) {
		super();
		this.method = method;
		this.annotations = annotations;
	}
	
	/**
	 * Checks if the preconditions provided by the capabilities of this are derivable by <br>
	 * the requested preconditions. <br>
	 * To determine whether the preconditions are derivable the types of each a formal parameter f and an actual <br>
	 * parameter a are compared pairwise. True is returned if for all i (1,...,parameter-count) parameters ai <= fi  
	 * @param actualParameters request's actual parameters
	 * @return true <=> request.capabilities.preconditions |- this.capabilities.preconditions 
	 */
	public boolean matchPreconditions(List<Object> actualParameters) {
		Class<?>[] formalParams = this.method.getParameterTypes();
		if (formalParams.length != actualParameters.size()) {
			return false;
		}
		for (int i = 0; i < formalParams.length; i++) {
			Class<?> actualParamClass = actualParameters.get(i).getClass();
			if (!formalParams[i].isAssignableFrom(actualParamClass)) {
				return false;
			}
		}
		return true;
	}
	
	public boolean matchPostconditions(Disjunction requestedSematicConditions, Class<?> requestedReturnType) {
		return  this.matchReturnType(requestedReturnType) 
				&& this.matchSemanticConditions(requestedSematicConditions);
	}
	
	private boolean matchSemanticConditions(
			Disjunction requestedSematicConditions) {
		return this.annotations.derives(requestedSematicConditions);
	}
	
	private boolean matchReturnType(final Class<?> requestedReturnType) {
		return requestedReturnType.isAssignableFrom(this.method.getReturnType()); 
	}

	public Object invoke(Object service, List<Object> actualParameters) throws ExecutionException {
		try {
			return this.method.invoke(service, actualParameters.toArray());
		} catch (Exception e) {
			throw new ExecutionException("Could not invoke " + this.method.getName(), e);
		}
	}
}
